/*
 @Name: template插件
 @Author: ray
 @License: MIT
 */
; rayui.define(function (exports) {
    "use strict";

    var plugName = "template",
        cache = {},
        gbIndex = 0,
        doc = document,
        escapeHtml = function (sHtml) {
            if (typeof sHtml !== "string") return sHtml;
            return sHtml.replace(/[<>&"']/g, function (c) { return { '<': '&lt;', '>': '&gt;', '&': '&amp;', '"': '&quot;', '\'': '&#39;' }[c]; });
        },
        makeReg = function (str) {
            return new RegExp(str, 'g');
        },
        error = function (e, tplcontent) {
            var str = 'template error：' + e;
            typeof console === 'object' && console.error(str + '\n' + (tplcontent || ''));
            return str;
        },
        getIndex = function (content) {
            for (var key in cache) {
                if (cache[key].content === content) return key;
            }
            return gbIndex++;
        },
        Tmpl = function (options) {
            //处理id模板
            if (options.content.indexOf('#') === 0)
                options.content = doc.getElementById(options.content.substr(1)).innerHTML;

            this.options = $.extend({}, Tmpl.option, options);
        };

    Tmpl.option = {
        open: '{{',
        close: '}}'
    }

    /**
     * 编译
     */
    Tmpl.prototype.compile = function () {

        var options = this.options,
            index = getIndex(this.options.content);
        if (!cache.hasOwnProperty(index)) {
            //each添加花括号


            var ss = [
                '"use strict";',

                'var p = [];',
                'p.push(\'',
                options.content
                    .replace(/[\r\t\n]/g, " ")// 去除换行制表符\t\n\r
                    .split(options.open).join("\t")// 将左分隔符变成 \t
                    //.replace(/((^|}})[^\t]*)'/g, "$1\r")// 去掉模板中单引号的干扰，\r回车
                    .replace(/'/g, "\r")// 上面一句修改为本句
                    .replace(/\t *each +(\b[^{^ ]+\b) +as +(\b[^{^ ]+\b) +(\b[^{^ ]+\b)(?=[{| |}])/g, '\t for(var $3=0,$2,len=$1.length;$3<len,($2=$1[$3]);$3++)')//each as语法支持
                    .replace(/\t *each +(\b[^{^ ]+\b)(?=[{| |}])/g, '\t for(var index=0,value,len=$1.length;index<len,(value=$1[index]);index++)')//each语法支持
                    .replace(makeReg("\t=#(.*?)" + options.close), "',$1,'")//把{{=# }}格式替换成str.push(a,b,c)格式，不转义
                    .replace(makeReg("\t=(.*?)" + options.close), "',escapeHtml($1),'")//把{{= }}格式替换成str.push(a,b,c)格式，转义
                    .split("\t").join("');")
                    .split(options.close).join(" p.push(\'")//p.push前面的空格不能删掉，因为不能保证用户的模板js代码后有空格
                    .split("\r").join("\\'"),
                '\');',
                'return p.join(\'\');'
            ].join('');
             
            var func = new Function("d", "escapeHtml", ss);
            func.content = this.options.content;
            cache[index] = func;
        }
        return cache[index];
    }

    var handle = {
        options: Tmpl.option,
        /**
         * 入口渲染
         * @param {any} content string或object类型，为object时必须含有tpl参数且必须为string类型，
         * @param {any} callback 回调函数，返回字符串，如果返回字符串则会被替代
         */
        render: function (content, data) {
            var options = content;
            if (typeof content === "string") {
                options = {
                    content: content,
                    data: data
                }
            }

            var hand = new Tmpl(options);
            var tplFunc = hand.compile();
            try {
                var str = tplFunc(options.data, escapeHtml);
                return str;
            } catch (e) {
                return error(e, options.content);
            }
        }

    }
    
    exports(plugName, handle);
}, rayui.jsAsync());
